#include "CoordinateSystemShader.h"
#include <glad/glad.h>
#include <cstring>
#include <glm/ext.hpp>
#include <glm/glm.hpp>
#include <iostream>
#include <string>
#include "glm/ext/matrix_transform.hpp"
#include "glm/fwd.hpp"
#include "glm/trigonometric.hpp"
using namespace glm;
using namespace std;

/**
c_str() 返回临时指针
std::string::c_str() 返回的是字符串内部的 临时指针，当 vertexShaderSource 和
fragmentShaderSource 这两个 std::string
对象离开构造函数作用域后，其内存可能被释放，导致保存的 char* 指针指向无效内存。

浅拷贝问题
你的代码只是保存了指针，而没有复制指针指向的实际字符串内容。当原 std::string
被销毁后，访问 this->vertexShaderSource 就是未定义行为（可能为空或乱码）。
*/
CoordinateSystemShader::CoordinateSystemShader(const string& vertexShaderSource,
                                               const string& fragmentShaderSource)
{
    this->vertexShaderSource = strdup(vertexShaderSource.c_str());
    this->fragmentShaderSource = strdup(fragmentShaderSource.c_str());
}

CoordinateSystemShader::CoordinateSystemShader()
{
    vertexShaderSource = "#version 330 core         \n\
            layout ( location = 0 ) in vec3 aPos;   \n\
            uniform   mat4 model;                   \n\
            uniform   mat4 view;                    \n\
            uniform   mat4 projection;              \n\
            void    main(){                         \n\
                gl_Position = projection * view * model * vec4(aPos,1.0);      \n\
        }\0";

    fragmentShaderSource = "#version 330 core       \n\
                            out vec4 FragColor;     \n\
                            void main(){            \n\
                                FragColor = vec4(1.0,0.7,0,1);    \n\
        }\0";
}

unsigned int CoordinateSystemShader::createShaderProgram()
{
    unsigned int shader = ShaderProgram::createShaderProgram();

    modelMatrixLocation = glGetUniformLocation(shader, "model");
    if (modelMatrixLocation == -1)
    {
        cout << "failed to get uniform mat4 model" << endl;
    }

    viewMatrixLocation = glGetUniformLocation(shader, "view");
    if (viewMatrixLocation == -1)
    {
        cout << "failed to get uniform mat4 view" << endl;
    }

    projectionMatrixLocation = glGetUniformLocation(shader, "projection");
    if (projectionMatrixLocation == -1)
    {
        cout << "failed to get uniform mat4 projection" << endl;
    }

    cout << "shader id is " << shader << endl;

    return shader;
}

/*
The output of the vertex shader requires the coordinates to be in clip-space
which is what we just did with the transformation matrices. OpenGL then performs
perspective division on the clip-space coordinates to transform them to
normalized-device coordinates. OpenGL then uses the parameters from glViewPort
to map the normalized-device coordinates to screen coordinates where each
coordinate corresponds to a point on your screen (in our case a 800x600 screen).
This process is called the viewport transform.
*/
void CoordinateSystemShader::update()
{
    mat4 model = createModelMatrix();
    mat4 view = createViewMatrix();
    mat4 projection = createClipSpace();

    // cout << "model matrix is " << model.length() <<endl;
    // cout << "view matrix is " << view.length() <<endl;
    // cout << "projection matrix is " << projection.length() <<endl;

    glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, value_ptr(model));
    glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, value_ptr(view));
    glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE,
                       value_ptr(projection));
    // 之后 openGL 会使用之前 glViewPort() 中设置的参数，映射NDC坐标映射到屏幕上
    // glViewPort()设置的 800*600
}

mat4 CoordinateSystemShader::createModelMatrix()
{

    mat4 unitMatrix = mat4(1.0f);

    // 绕x轴旋转55度，让其看起来像是躺在地面上一样
    // 这样来模拟把物体搬运到世界坐标系下
    return rotate(unitMatrix, radians(-80.0f), vec3(1.0f, 0.0f, 0.0f));
}

/**
    把物体从世界坐标系变换到摄像机坐标系下
    想一想：把摄像机向后移动  等价于 把物体往前移动
    To move a camera backwards, is the same as moving the entire scene forward.
    That is exactly what a view matrix does, we move the entire scene around
   inversed to where we want the camera to move.
   Because we want to move backwards and since OpenGL is a right-handed system
   we have to move in the positive z-axis. We do this by translating the scene
   towards the negative z-axis. This gives the impression that we are moving
   backwards.
*/
mat4 CoordinateSystemShader::createViewMatrix()
{
    mat4 view = mat4(1.0f);

    // note that we're translating the scene in the reverse direction of where
    // we want to move
    return translate(view, vec3(0.0f, 0.0f, -3.0f));
}

mat4 CoordinateSystemShader::createPerspectiveProjection()
{

    return perspective(
        /**
        Its first parameter defines the fov value, that stands for field of view
        and sets how large the viewspace is. For a realistic view it is usually
        set to 45 degrees, but for more doom-style results you could set it to a
        higher value.
        */
        radians(45.0f),
        /*
        The second parameter sets the aspect ratio which is calculated by
        dividing the viewport's width by its height.
        */
        800.0f / 600.0f,
        /*
        The third and fourth parameter set the near and far plane of the
        frustum. We usually set the near distance to 0.1 and the far distance to
        100.0. All the vertices between the near and far plane and inside the
        frustum will be rendered.
        */
        0.1f, 100.0f);
}